package com.inspur.edp.lcm.metadata.extend.action;

import com.google.common.collect.Lists;
import com.inspur.edp.lcm.delivery.api.entity.Deliverable;
import com.inspur.edp.lcm.delivery.spi.DeliverableCollectSpi;
import com.inspur.edp.lcm.delivery.spi.args.DeliverableCollectArgs;
import com.inspur.edp.lcm.delivery.spi.outcomes.DeliverableCollectOutcomes;
import com.inspur.edp.lcm.metadata.api.entity.GspProject;
import com.inspur.edp.lcm.metadata.api.exception.ErrorCodes;
import com.inspur.edp.lcm.metadata.api.exception.LcmFileDevException;
import com.inspur.edp.lcm.metadata.api.exception.LcmMetadataDevException;
import com.inspur.edp.lcm.metadata.api.service.*;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.ServiceUtils;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import com.inspur.edp.lcm.metadata.inner.api.utils.IdeLogUtils;
import io.iec.edp.caf.commons.utils.CollectionUtils;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.sumgr.api.entity.ServiceUnitInfo;
import io.iec.edp.caf.sumgr.core.ServiceUnitFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class MetadataDeliverableCollectImpl implements DeliverableCollectSpi {
    // TODO 补丁目录应使用补丁接口中的常量，目前他们没有
    private static final String ROOT_DIR = "root";
    private static final String DB_UPDATE_FILES_DIR = "DBUpdateFiles";
    private static final String METADATA_DIR = "MetaData";
    private static final String PATCH_DBO_DIR = "Dbo";
    private static final String WEB_DIR = "web";
    private static final String PUBLISH_DBO_PATH = "dbo";
    private static final String DBO_SUFFIX = ".dbo";
    private static final String SLASH = "/";
    private static final String APPS = "apps";
    private static final String MODULE_INIT = "moduleInit";

    MdpkgService mdpkgService;

    ProjectExtendService projectExtendService;

    MetadataProjectService metadataProjectService;

    GspProjectService gspProjectService;

    private FileService fileService;

    private MdpkgService getMdpkgService() {
        if (mdpkgService == null) {
            mdpkgService = SpringBeanUtils.getBean(MdpkgService.class);
        }
        return mdpkgService;
    }

    private ProjectExtendService getProjectExtendService() {
        if (projectExtendService == null) {
            projectExtendService = SpringBeanUtils.getBean(ProjectExtendService.class);
        }
        return projectExtendService;
    }

    private MetadataProjectService getMetadataProjectService() {
        if (metadataProjectService == null) {
            metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        }
        return metadataProjectService;
    }

    private GspProjectService getGspProjectService() {
        if (gspProjectService == null) {
            gspProjectService = SpringBeanUtils.getBean(GspProjectService.class);
        }
        return gspProjectService;
    }

    private FileService getFileService() {
        if (fileService == null) {
            fileService = SpringBeanUtils.getBean(FileService.class);
        }
        return fileService;
    }

    /**
     * 收集元数据交付物
     *
     * @param args 参数
     * @return data和dbo文档id
     */
    @Override
    public DeliverableCollectOutcomes collect(DeliverableCollectArgs args) {
        if (args == null || CollectionUtils.isEmpty(args.getDeliverables())) {
            return null;
        }
        // 元数据操作是按工程来处理的
        List<String> projPaths = args.getDeliverables().stream().map(Deliverable::getProjectPath).distinct().collect(Collectors.toList());
        // 对工程进行生成、编译、打包、提取
        operate(projPaths);
        // 然后把工程publish目录下的文件复制到tmpPatchPath
        boolean isModuleInit = MODULE_INIT.equals(args.getGspPatchTypeCode());
        for (String projPath : projPaths) {
            copyToPatch(projPath, args.getTmpPatchPath());
            // 模块初始补丁，生成service-unit.yaml
            if (isModuleInit) {
                generateSuFile(args.getTmpPatchPath(), projPath);
            }
        }
        return null;
    }

    private void operate(List<String> projPaths) {
        IdeLogUtils.setStageStatus(IdeLogUtils.SUCCESS);
        try {
            // 批量生成代码、maven编译
            List<String> operationList = Lists.newArrayList(Utils.GENERATE, Utils.COMPILE);
            getMdpkgService().batchOperate(projPaths, operationList);
            if (!IdeLogUtils.SUCCESS.equals(IdeLogUtils.getStageStatus())) {
                throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0041);
            }
            // 批量执行扩展编译
            try {
                getProjectExtendService().batchCompile(projPaths, "", Utils.JITTODLL);
            } catch (Exception e) {
                throw new LcmMetadataDevException(e, ErrorCodes.ECP_METADATA_DEV_0042);
            }
            if (!IdeLogUtils.SUCCESS.equals(IdeLogUtils.getStageStatus())) {
                throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0042);
            }
            // 批量提取
            getProjectExtendService().extract(projPaths);
            if (!IdeLogUtils.SUCCESS.equals(IdeLogUtils.getStageStatus())) {
                throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0043);
            }
        } finally {
            IdeLogUtils.removeStageStatus();
        }
    }

    private void copyToPatch(String projPath, String tmpPatchPath) {
        projPath = ManagerUtils.getAbsolutePath(projPath);
        String publishPath = FileServiceImp.combinePath(getMetadataProjectService().getProjPath(projPath), Utils.getPublishPath());
        //获取publish目录下dbo文件
        copyDboFile(publishPath, tmpPatchPath);
        // 获取目录，应为server、metadata、web
        File[] dirs = new File(publishPath).listFiles(File::isDirectory);

        for (File dir : dirs) {
            // 元数据包
            if (Utils.getMetadataProjPath().equals(dir.getName())) {
                copyMdpkg(dir, tmpPatchPath);
                continue;
            }
            // 补丁中的文件目录，可能有server、web，在root中
            String patchServerWebPath = FileServiceImp.combinePath(tmpPatchPath, ROOT_DIR, dir.getName());
            if (Utils.getServerPath().equals(dir.getName()) || WEB_DIR.equals(dir.getName())) {
                // copy server or web
                getFileService().folderCopy(dir.getAbsolutePath(), patchServerWebPath);
            }

            // jar包
            if (Utils.getServerPath().equals(dir.getName())) {
                // 把dbo挑出来
                moveDbo(patchServerWebPath);

            }
        }
    }

    private void copyDboFile(String publishPath, String tmpPatchPath) {
        Path patchDboPath = Paths.get(tmpPatchPath, DB_UPDATE_FILES_DIR, PATCH_DBO_DIR);
        try (Stream<Path> stream = Files.walk(Paths.get(publishPath))){
            // 过滤publish目录下的dbo文件
            List<Path> dboPathList = stream.filter(path -> path.toFile().isFile() && path.getFileName().toString().endsWith(DBO_SUFFIX)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(dboPathList)) {
                return;
            }
            // 创建补丁dbo目录
            if (!patchDboPath.toFile().exists()) {
                patchDboPath.toFile().mkdirs();
            }
            // 复制dbo文件
            for (Path dboPath : dboPathList) {
                getFileService().fileCopy(dboPath.toString(), patchDboPath.resolve(dboPath.getFileName()).toString());
            }
        } catch (IOException e) {
            throw new LcmFileDevException(e, ErrorCodes.ECP_FILE_DEV_0006, Paths.get(publishPath).toString());
        }
    }

    private void copyMdpkg(File dir, String tmpPatchPath) {
        // 补丁中的元数据目录
        Path patchMetadataPath = Paths.get(tmpPatchPath, DB_UPDATE_FILES_DIR, METADATA_DIR);
        try (Stream<Path> stream = Files.walk(dir.toPath())){
            // 过滤元数据包，元数据包可能是多个，提取依赖的时候
            List<Path> mdpkgPathList = stream.filter(path -> path.toFile().isFile() && path.getFileName().toString().endsWith(ServiceUtils.getMetadataPackageExtension())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(mdpkgPathList)) {
                return;
            }
            // 创建补丁元数据包目录
            if (!patchMetadataPath.toFile().exists()) {
                patchMetadataPath.toFile().mkdirs();
            }
            // 复制元数据包
            for (Path mdpkgPath : mdpkgPathList) {
                getFileService().fileCopy(mdpkgPath.toString(), patchMetadataPath.resolve(mdpkgPath.getFileName()).toString());
            }
        } catch (IOException e) {
            throw new LcmFileDevException(e, ErrorCodes.ECP_FILE_DEV_0006, dir.getPath());
        }
    }

    private void moveDbo(String patchServerPath) {
        List<File> dboDirs = new ArrayList<>();
        List<File> dboFiles = new ArrayList<>();
        try (Stream<Path> stream = Files.walk(Paths.get(patchServerPath))) {
            // 复制所有的dbo
            stream.forEach(path -> {
                if (path.toFile().isFile() && path.getFileName().toString().endsWith(DBO_SUFFIX)) {
                    dboFiles.add(path.toFile());
                } else if (path.toFile().isDirectory() && PUBLISH_DBO_PATH.equals(path.toFile().getName())) {
                    // 把所有的dbo目录找出来删掉，dbo目录不需要部署到环境上
                    dboDirs.add(path.toFile());
                }
            });
        } catch (IOException e) {
            throw new LcmFileDevException(e, ErrorCodes.ECP_FILE_DEV_0006, patchServerPath);
        }
        // 删除dbo文件
        if (!CollectionUtils.isEmpty(dboFiles)) {
            for (File dboFile : dboFiles) {
                dboFile.delete();
            }
        }
        // 删除dbo目录
        if (!CollectionUtils.isEmpty(dboDirs)) {
            for (File dboDir : dboDirs) {
                dboDir.delete();
            }
        }
    }

    private void generateSuFile(String tmpPatchPath, String projPath) {
        GspProject gspProjectInfo;
        try {
            gspProjectInfo = getGspProjectService().getGspProjectInfo(projPath);
        } catch (IOException e) {
            throw new LcmMetadataDevException(e, ErrorCodes.ECP_METADATA_DEV_0004, projPath);
        }
        String destDir = FileServiceImp.combinePath(tmpPatchPath, ROOT_DIR, Utils.getServerPath(), APPS, gspProjectInfo.getAppCode().toLowerCase(), gspProjectInfo.getServiceUnitCode().toLowerCase());
        File file = new File(destDir);
        if (!ServiceUnitFactory.createGenerator().exists(file)) {
            ServiceUnitInfo serviceUnitInfo = new ServiceUnitInfo();
            serviceUnitInfo.setApplicationName(gspProjectInfo.getAppCode());
            serviceUnitInfo.setName(gspProjectInfo.getServiceUnitCode());
            serviceUnitInfo.setPath(SLASH + APPS + SLASH + gspProjectInfo.getAppCode().toLowerCase() + SLASH + gspProjectInfo.getServiceUnitCode().toLowerCase());
            ServiceUnitFactory.createGenerator().generate(serviceUnitInfo, file);
        }
    }
}
